package small.common.core.spi.extension.interceptor;

import small.common.core.spi.extension.Adaptive;
import small.common.core.spi.extension.Extension;
import small.common.core.spi.extension.ExtensionLoader;
import small.common.core.util.StringUtil;
import small.common.designPattern.structure.proxy.AopMethodInterceptor;
import small.common.designPattern.structure.proxy.AopMethodInvocation;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;

/**
 * @author jinzhou
 * @data 2023/4/14 13:29
 */
public class ExtensionLoaderInterceptor implements AopMethodInterceptor {

    @Override
    public Object invoke(AopMethodInvocation methodInvocation) throws Throwable {

        Method method = methodInvocation.getMethod();
        Object[] arguments = methodInvocation.getArguments();
        Class<?> targetClass = methodInvocation.getTargetClass();

        if (!method.isAnnotationPresent(Adaptive.class)) {
            return methodInvocation.proceed();
        }
        Adaptive annotation = method.getAnnotation(Adaptive.class);
        int urlTypeIndex = getUrlTypeIndex(method);

        Extension extension = null;
        if (urlTypeIndex != -1) {
            extension = (Extension) arguments[urlTypeIndex];
        } else {//没有发现Extension
            extension = generateUrlAssignmentIndirectly(methodInvocation);
        }
        Map<String, String> parameters = extension.getParameters();
        String[] methodAdaptiveValue = getMethodAdaptiveValue(annotation,targetClass);
        String extName = null;
        for (String parameterName : methodAdaptiveValue) {
            if (parameters.containsKey(parameterName)) {
                extName = parameters.get(parameterName);
                break;
            }
        }

        if (StringUtil.isBlank(extName)){
            throw new IllegalStateException("Not found ExtensionName from parameters use keys("+ Arrays.toString(methodAdaptiveValue) +")");
        }

        Object loader = ExtensionLoader.getExtensionLoader(methodInvocation.getTargetClass()).getExtension(extName);
        if (loader == null) {
            if (annotation.hasExtensionPoint()) {
                throw new IllegalStateException("Failed to get extension ("+methodInvocation.getTargetClass().getName()+")  use keys("+ Arrays.toString(methodAdaptiveValue) +")");
            }
            return methodInvocation.proceed();
        }
        //执行拓展方法
        return method.invoke(loader, methodInvocation.getArguments());
    }


    /**
     * 获取方法参数中的Extension类型的下标（只取第一个）
     * -1 表示没找到
     *
     * @param method
     * @return
     */
    private int getUrlTypeIndex(Method method) {
        int urlTypeIndex = -1;
        Class<?>[] pts = method.getParameterTypes();
        for (int i = 0; i < pts.length; ++i) {
            if (Extension.class.isAssignableFrom(pts[i])) {
                urlTypeIndex = i;
                break;
            }
        }
        return urlTypeIndex;
    }

    /**
     * 从参数列表中获取Extension
     *
     * @param methodInvocation
     * @return
     */
    private Extension generateUrlAssignmentIndirectly(AopMethodInvocation methodInvocation) {
        Method method = methodInvocation.getMethod();
        Object[] arguments = methodInvocation.getArguments();
        Class<?>[] pts = method.getParameterTypes();

        // find URL getter method
        for (int i = 0; i < pts.length; ++i) {
            for (Method m : pts[i].getMethods()) {
                String name = m.getName();
                //判断条件：
                //1. 方法名称是getExtension
                //2. 公共方法
                //3. 非静态方法
                //4. 没有方法参数
                //5. 返回值是Extension类型
                if ((name.equals("getExtension"))
                        && Modifier.isPublic(m.getModifiers())
                        && !Modifier.isStatic(m.getModifiers())
                        && m.getParameterTypes().length == 0
                        && m.getReturnType().isAssignableFrom(Extension.class)) {
                    try {
                        m.setAccessible(true);
                        return (Extension) m.invoke(arguments[i]);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
        throw new IllegalStateException("Failed to create adaptive class for interface " + methodInvocation.getTargetClass().getName()
                + ": not found Extension parameter or Extension attribute in parameters of method " + method.getName());
    }

    /**
     * 获取拓展名
     *
     * @param adaptiveAnnotation
     * @return
     */
    private String[] getMethodAdaptiveValue(Adaptive adaptiveAnnotation,Class<?> targetClass) {
        String[] value = adaptiveAnnotation.value();
        if (value.length == 0) {
            //默认去接口简单类名小写
            String splitName = StringUtil.firstCharToLowerCase(targetClass.getSimpleName());
            value = new String[]{splitName};
        }
        return value;
    }
}
